/**
* Oxide
*
* Copyright (c) 2011 Dinosaur Kick Pty Ltd, All rights reserved.
*
* This source code is made available under the "Modified BSD" licensing arrangement.
* See license.txt for details.
*/
// ----------------------------------------------------------------------------
#include <string>
// #include <boost/foreach.hpp>
// #include <boost/lexical_cast.hpp>
// #include <boost/algorithm/string.hpp>
// #include "oxide-common/ast/ast_fwd.hpp"
#include "cstyle_renderer.hpp"
// ----------------------------------------------------------------------------
#ifndef O2_RENDERER_OXIDE_RENDERER
#define O2_RENDERER_OXIDE_RENDERER
namespace o2 {
namespace renderer {
// ----------------------------------------------------------------------------

template <typename StringT>
class basic_oxide_renderer
	: public basic_cstyle_renderer<basic_oxide_renderer<StringT>, StringT>
{
	public:
		typedef StringT string_type;
		O2_AST_TYPEDEFS(string_type);

		string_type visit(const root_type& node)
		{
			return node.class_
				? this->apply(*node.class_)
				: string_type();
		}

		string_type visit(const class_decl_type& node)
		{
			string_type result;

			if (!node.doc_block.empty())
			{
				result += this->indent();
				result += node.doc_block;
				result += O2_CSTR("\n");
			}

			result += this->indent();
			result += O2_CSTR("class ");

			if (node.is_abstract)
				result += O2_CSTR("abstract ");

			if (node.is_final)
				result += O2_CSTR("final ");

			result += boost::algorithm::join(node.namespace_atoms, O2_CSTR("."));
			result += O2_CSTR(".");
			result += node.name;
			result += O2_CSTR("\n");
			result += this->open_block('{');

			BOOST_FOREACH(const typename class_member_map::value_type& pair, node.members)
				result += this->apply(*pair.second);

			result += this->close_block();
			return result;
		}
		
		string_type visit(const method_type& node)
		{
			string_type result = this->indent();
			result += node.access;
			result += O2_CSTR(" method ");
			result += node.type
				? this->apply(*node.type)
				: O2_CSTR("<unknown>");
			result += O2_CSTR(" ");
			result += node.name;
			result += O2_CSTR("()");
			result += node.stmt_block
				? O2_CSTR("\n") + this->apply(*node.stmt_block)
				: O2_CSTR(";\n");
			return result;
		}
		
		string_type visit(const member_var_type& node)
		{
			string_type result = this->indent();
			result += node.access;
			result += O2_CSTR(" var ");
			result += node.type
				? this->apply(*node.type)
				: O2_CSTR("<unknown>");
			result += O2_CSTR(" ");
			result += node.name;

			if (node.default_value)
			{
				result += O2_CSTR(" = ");
				result += this->apply(*node.default_value);
			}

			result += O2_CSTR(";\n");
			return result;
		}
		
		string_type visit(const string_literal_type& node)
		{
			return O2_CSTR("\"") + node.value + O2_CSTR("\"");
		}
		
		string_type visit(const integer_literal_type& node)
		{
			return boost::lexical_cast<string_type>(node.value);
		}
		
		string_type visit(const float_literal_type& node)
		{
			return boost::lexical_cast<string_type>(node.value);
		}
		
		string_type visit(const fixed_literal_type& node)
		{
			return boost::lexical_cast<string_type>(node.value);
		}
		
		string_type visit(const stmt_block_type& node)
		{
			string_type result = this->open_block('{');

			BOOST_FOREACH(stmt_ptr stmt, node.statements)
			{
				result += this->indent();
				result += this->apply(*stmt);
				result += O2_CSTR(";\n");
			}
			
			result += this->close_block();
			return result;
		}
		
		string_type visit(const identifier_type& node)
		{
			return node.value;
		}
		
		string_type visit(const binary_oper_type& node)
		{
			return O2_CSTR("(")
				 + this->apply(*node.lhs)
				 + O2_CSTR(" ")
				 + token_identity_name<string_type>(node.oper)
				 + O2_CSTR(" ")
				 + this->apply(*node.rhs)
				 + O2_CSTR(")");
		}

		string_type visit(const unary_oper_type& node)
		{
			string_type result = O2_CSTR("(");

			if (node.postfix)
			{
				result += this->apply(*node.operand);
				result += O2_CSTR(" ");
				result += token_identity_name<string_type>(node.oper);
			}
			else
			{
				result += token_identity_name<string_type>(node.oper);
				result += O2_CSTR(" ");
				result += this->apply(*node.operand);
			}

			result += O2_CSTR(")");
			return result;
		}

		string_type visit(const call_expr_type& node)
		{
			string_type result = this->apply(*node.callable);
			result += O2_CSTR("(");

			bool first = true;
			BOOST_FOREACH(expr_ptr expr, node.arguments)
			{
				if (first)
					first = false;
				else
					result += O2_STR(", ");
				result += this->apply(*expr);
			}

			result += O2_CSTR(")");
			return result;
		}

};

// ----------------------------------------------------------------------------

typedef basic_oxide_renderer<std::string> oxide_renderer;
typedef basic_oxide_renderer<std::wstring> woxide_renderer;

// ----------------------------------------------------------------------------
} // namespace renderer
} // namespace o2
#endif // O2_RENDERER_OXIDE_RENDERER
// ----------------------------------------------------------------------------
